package com.karateca.columbiaimports;

import java.text.NumberFormat;

/**
 * Used to calculate statistics for a download process. The process has to work
 * on items. It has a start time and an end time.
 * @author Andres Dominguez
 */
public class DownloadStatistics
{
    /** Keeps the start time of the process */
    private long startTime;
    /** Keeps the end time of the process */
    private long endTime;
    /** Number of items being processed */
    private int itemCount;
    /** Latest count of remainint items. Used to calculate delta */
    private int latestCount;
    /** Latest delta that was calculated when updateCount method was called */
    private int latestDelta;
    /** Number of consecutive zero-deltas when the update method is called */
    private int zeroDeltaCount;

    /**
     * Creates a new instance of DownloadStatistics.
     * @param itemCount number of items to be processed.
     */
    public DownloadStatistics(int itemCount)
    {
        this.itemCount = itemCount;
        this.latestCount = itemCount;
        this.latestDelta = 0;
        this.zeroDeltaCount = 0;
        start();
    }

    /**
     * Start timer.
     */
    private void start()
    {
        startTime = System.currentTimeMillis();
        this.zeroDeltaCount = 0;
    }

    /**
     * End timer.
     */
    public void stop()
    {
        endTime = System.currentTimeMillis();
    }

    /**
     * Calculate the number of seconds since start time.
     * @return number of seconds since start.
     */
    public int runningSeconds()
    {
        long delta;

        stop();
        delta = this.endTime - this.startTime;

        return (int) delta / 1000;
    }

    /**
     * Update the number of remaning elements to be processed.
     * @param currentCount remaining elements.
     * @return delta between the las update.
     */
    public int updateCount(int currentCount)
    {
        int delta;

        stop();
        delta = this.latestCount - currentCount;
        this.latestCount = currentCount;
        this.latestDelta = delta;

        // update zero delta count
        if(delta == 0)
        {
            this.zeroDeltaCount++;
        }
        else
        {
            this.zeroDeltaCount = 0;
        }
        return delta;
    }

    /**
     * Calculate the completed percentage.
     * @return percent complete.
     */
    public String percentComplete()
    {
        double total;
        double previous;
        double percentage;
        NumberFormat format;

        format = NumberFormat.getPercentInstance();
        format.setMaximumFractionDigits(1);
        format.setMinimumFractionDigits(1);


        total = this.itemCount;
        previous = this.latestCount;
        percentage = (total - previous) / total;

        return format.format(percentage);
    }

    /**
     * Calculate the number of minutes since start time.
     * @return number of minutes since start.
     */
    public int runningMinutes()
    {
        return runningSeconds() / 60;
    }

    /**
     * Get the running time (hours:minutes) as a string.
     * @return the running time of the process.
     */
    public String getRunningTime()
    {
        long deltaTime;
        String runningTime;
        long hours;
        String minutes;

        stop();
        deltaTime = this.endTime - this.startTime;
        minutes = String.valueOf((deltaTime / (60 * 1000)) % 60);
        hours = deltaTime / (60 * 60 * 1000);

        if(minutes.length() == 1)
        {
            minutes = "0" + minutes;
        }
        runningTime = "" + hours + ":" + minutes;

        return runningTime;
    }

    /**
     * Calculate the estimate time to comple te process (in minutes).
     * @return the estimated number of minutes to complete the process.
     */
    public int estimatedTime()
    {
        int rate;

        rate = ratePerMinute();

        if (rate == 0)
        {
            return -1;
        }
        return this.latestCount / rate;
    }

    /**
     * Estimates the time that will take to complete the process given the current
     * rate. The estimated time is given in hours:minutes.
     * @return the estimated remaining time with the following format hh:mm.
     */
    public String timeRemaining()
    {
        String result;
        int hours;
        String minutes;

        if(estimatedTime() == -1)
        {
            return "--:--";
        }
        hours = estimatedTime() / 60;
        minutes = String.valueOf(estimatedTime() % 60);
        if(minutes.length() == 1)
        {
            minutes = "0" + minutes;
        }

        // Add extra 0 to the end if missing
        result = "" + hours + ":" + minutes;

        return result;
    }

    /**
     * Calculate the number of downloads per minute.
     * @return the number of downloads per minute.
     */
    public int ratePerMinute()
    {
        double rate;
        double seconds;
        double total;
        double previous;

        total = this.itemCount;
        previous = this.latestCount;

        seconds = runningSeconds();
        if (seconds == 0)
        {
            return 0;
        }
        rate = (total - previous) / (seconds / 60.0);

        return (int) rate;
    }

    /**
     * Create a string with the download statistics.
     * @return the download statistics.
     */
    public String getStatistics()
    {
        StringBuilder sb;

        sb = new StringBuilder();
        sb.append("Delta: " + this.latestDelta);
        sb.append(", " + percentComplete());
        sb.append(", rate: " + ratePerMinute() + " per min");
        sb.append(", time remaining " + timeRemaining());
        sb.append(", remaining: " + this.latestCount);

        return sb.toString();
    }

    /**
     * @return the zeroDeltaCount
     */
    public int getZeroDeltaCount()
    {
        return zeroDeltaCount;
    }

    /**
     * @param zeroDeltaCount the zeroDeltaCount to set
     */
    public void setZeroDeltaCount(int zeroDeltaCount)
    {
        this.zeroDeltaCount = zeroDeltaCount;
    }
}
